home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Compilers⁄Interps / kevoSource / tasks.c < prev    next >
Text File  |  1993-05-10  |  11KB  |  464 lines

  1. /* Kevo -- a prototype-based object-oriented language */
  2. /* (c) Antero Taivalsaari 1991-1993                   */
  3. /* Some parts (c) Antero Taivalsaari 1986-1988           */
  4. /* tasks.c: Task management internals                   */
  5.  
  6. #include "global.h"
  7.  
  8. /*--------------------------------------------------------------------------*/
  9. /* Multitasking primitives                                                    */
  10.  
  11. /* 
  12.     'yieldTo()' changes the desired task to be executed right now.
  13.     No checks are made whether the task is active, so you must be 
  14.     very careful in using this operation.
  15. */
  16. void yieldTo(thisTask)
  17. TASK** thisTask;
  18. {
  19.     /* Push earlier environment to the top of the current return stack */
  20.     storeExecEnv();
  21.  
  22.     up = thisTask;        /* Task switch occurs here!! */
  23.  
  24.     /* Load new environment from the current return stack */
  25.     loadExecEnv();
  26. }
  27.  
  28.  
  29. /* Store the current execution environment frame to the return stack */
  30. /* 
  31.     Note that since this operation changes the contents of the return 
  32.     stack, you cannot continue inner interpreter execution before 
  33.     executing the corresponding loadExecEnv() operation.
  34. */
  35. void storeExecEnv()
  36. {
  37. /* Depending on the machine architecture, select the faster implementation */
  38.     nPushReturn(3);
  39.     thirdReturn  = (int*)contextSp;
  40.     secondReturn = (int*)dataSp;
  41.     topReturn    = (int*)ip;
  42.     (*up)->rpStore = returnSp;
  43. /*
  44.     pushReturn((int*)contextSp);
  45.     pushReturn((int*)dataSp);
  46.     pushReturn((int*)ip);
  47.     (*up)->rpStore = returnSp;
  48. */
  49. }
  50.  
  51.  
  52. /* Load the previous execution environment frame from the return stack */
  53.  
  54. void loadExecEnv()
  55. {
  56. /* Depending on the machine architecture, select the faster implementation */
  57.     returnSp  = (*up)->rpStore;
  58.     ip          = (int**)topReturn;
  59.     dataSp      = secondReturn;
  60.     contextSp = (int**)thirdReturn;
  61.     nPopReturn(3);
  62. /*
  63.     returnSp  = (*up)->rpStore;
  64.     ip        = (int**)popReturn();
  65.     dataSp    = popReturn();
  66.     contextSp = (int**)popReturn();
  67. */
  68. }
  69.  
  70. /* buildTask(): build a new background task */
  71. /* The task shares the window with its creator */
  72.  
  73. TASK** buildTask()
  74. {
  75.   /* 
  76.     We copy the current task so the new task "inherits" all the
  77.     properties from the current task. 
  78.   */
  79.   TASK** newTask = (TASK**)copyObject(up);
  80.   TASK** tempUp;
  81.  
  82.   /* New task needs its own execution stacks */
  83.   (*newTask)->returnStack  = copyObject((*up)->returnStack);
  84.   (*newTask)->dataStack    = copyObject((*up)->dataStack);
  85.   (*newTask)->contextStack = copyObject((*up)->contextStack);
  86.  
  87.   /* New task has its own execution and textBuffer areas */
  88.   (*newTask)->trampoline   = copyObject((*up)->trampoline);
  89.   (*newTask)->textBuffer   = copyObject((*up)->textBuffer);
  90.  
  91.   /* New task has also its own file stacks */
  92.   (*newTask)->infileStack  = copyObject((*up)->infileStack);
  93.   (*newTask)->outfileStack = copyObject((*up)->outfileStack);
  94.  
  95.   /* Initialize the text buffer */
  96.   eraseKeyBuffer(newTask);
  97.  
  98.   /* Priority will be initialized to base priority */
  99.   (*newTask)->priority       = basePriority;
  100.  
  101.   /* Set the input and output files to 0 (terminal) */
  102.   tempUp = up;
  103.   up = newTask;
  104.     infile  = 0;
  105.     outfile = 0;
  106.     errfile = 0;
  107.     infileSp  = 0;
  108.     outfileSp = 0;
  109.   up = tempUp;
  110.  
  111.   /* Set the initial behavior of the task to be 'boot' */
  112.   setTaskBehavior(newTask, oBoot);
  113.  
  114.   /* Update the task link and number of tasks */
  115.   (*latestTask)->nextTask  = newTask;
  116.   (*newTask)->nextTask     = NIL;
  117.   latestTask = newTask;
  118.   taskCount++;
  119.  
  120.   return(newTask);
  121. }
  122.  
  123.  
  124. /* previousRunningTask(): return the task that has its execution turn */
  125. /* right before the given task (is located in front of it in the RR-chain) */
  126.  
  127. TASK** previousRunningTask(task)
  128. TASK** task;
  129. {
  130.   TASK** thisTask = (*up)->nextInRobin;
  131.   TASK** prevTask = up;
  132.  
  133.   /* 
  134.      Special case: If there is only one task in the round-robin chain, 
  135.      and that is the requested task, return it.
  136.   */
  137.   if (thisTask == prevTask) {
  138.      if (task == thisTask) return(up);
  139.      else return(FALSE);
  140.   }
  141.  
  142.   while (thisTask != up) {
  143.     if (task == thisTask) return(prevTask);
  144.     prevTask = thisTask;
  145.     thisTask = (*thisTask)->nextInRobin;
  146.   }
  147.  
  148.   if (task == thisTask) return(prevTask);
  149.   else return(FALSE);
  150. }
  151.  
  152.  
  153. /* isActivated(): check if a task is already in the round-robin chain */
  154.  
  155. int isActivated(task)
  156. TASK** task;
  157. {
  158.   TASK** thisTask = (*up)->nextInRobin;
  159.  
  160.   /* 
  161.      Special case: If there is only one task in the round-robin chain, 
  162.      and that is the requested task.
  163.   */
  164.   if (task == up) return(TRUE);
  165.  
  166.   while (thisTask != up) {
  167.     if (task == thisTask) return(TRUE);
  168.     thisTask = (*thisTask)->nextInRobin;
  169.   }
  170.  
  171.   return(FALSE);
  172. }
  173.  
  174.  
  175. /* Activate a task, allowing the task to continue its previous execution */
  176. /* This operations operates only if the task is not already in the */
  177. /* round-robin chain. */
  178.  
  179. void activateTask(thisTask)
  180. TASK** thisTask;
  181. {
  182.     if (!isActivated(thisTask)) {
  183.           TASK** temp = (*up)->nextInRobin;
  184.           (*up)->nextInRobin = thisTask;
  185.           (*thisTask)->nextInRobin = temp;
  186.           runningCount++;
  187.     }
  188. }
  189.  
  190.  
  191. /* Suspend a task (cancel its execution until it is reactivated) by */
  192. /* removing it from the round-robin chain. This operation operates */
  193. /* only if the task is in the round-robin chain, and the task is */
  194. /* not the only running task in the system (because otherwise */
  195. /* the system would die). */ 
  196.  
  197. int suspendTask(thisTask)
  198. TASK** thisTask;
  199. {
  200.       if (!isActivated(thisTask)) return(TRUE);
  201.     
  202.     if ((runningCount == 1) || (!multitasking && thisTask == up)) {
  203.           /* Cannot suspend the only active task in the system */
  204.         return(FALSE);
  205.     }
  206.     else {
  207.           TASK** prevTask; 
  208.           prevTask = previousRunningTask(thisTask);
  209.         (*prevTask)->nextInRobin = (*thisTask)->nextInRobin;
  210.         runningCount--;
  211.         return(TRUE);
  212.       }
  213. }
  214.     
  215.  
  216. /* Suspend a task and yield to the next task automatically */
  217.  
  218. int yieldingSuspend(thisTask)
  219. TASK** thisTask;
  220. {
  221.   int success;
  222.  
  223.     if (runningCount == 1 && thisTask == up) return(FALSE);
  224.  
  225.     /* If singletasking and there are other active tasks available */
  226.     if (!multitasking && thisTask == up && runningCount > 1) {
  227.         yieldTo((*up)->nextInRobin);
  228.         success = suspendTask(thisTask);
  229.     }
  230.     else {
  231.         success = suspendTask(thisTask);
  232.         yield();
  233.     }
  234.  
  235.     return(success);
  236. }  
  237.  
  238.  
  239. /* Reset the behavior of a task. The task must be in suspended state. */
  240. /* The code to be executed is given as an object (handle). */
  241.  
  242. void setTaskBehavior(thisTask, behavior)
  243. TASK**    thisTask;
  244. OBJECT* behavior;
  245. {
  246.   TASK**  tempUp = up;
  247.   int*      currentContext = topContext;
  248.  
  249.      if (isActivated(thisTask)) return;
  250.  
  251.     /* 
  252.         Change 'up' temporarily to 'thisTask' to allow easy initialization
  253.         of new task's execution environment.
  254.     */
  255.     storeExecEnv();
  256.     up = thisTask; 
  257.  
  258.     /* Reset the stack pointers and set the instruction pointer (ip) */
  259.     initStacks();
  260.     ip = (int**)behavior->mfa;
  261.     
  262.     /* Assignment count of the task must be zero initially */
  263.     (*thisTask)->assigning = 0;
  264.     
  265.     /* 
  266.         Context stack should normally never be empty.
  267.         Therefore, we push the current task's context to the 
  268.         context stack of the new task. 
  269.     */
  270.     pushContext(currentContext);
  271.  
  272.     /* 
  273.         Initialize the execution environment of the new task.
  274.         'Yield' will then load the environment when it is the new task's turn.
  275.     */
  276.     storeExecEnv();
  277.  
  278.     /* Finally, return to the current 'up' and execution environment. */
  279.     up = tempUp;
  280.     loadExecEnv();
  281. }
  282.  
  283.  
  284. /* Delete a task provided that it is not running */
  285.  
  286. int deleteTask(thisTask)
  287. TASK** thisTask;
  288. {
  289.     if (!isActivated(thisTask)) {
  290.         /* Remove the task from the task list */
  291.         if (thisTask == firstTask) firstTask = (*firstTask)->nextTask;
  292.         else {
  293.             TASK** prevTask = firstTask;
  294.             while (prevTask) {
  295.                 if (thisTask == (*prevTask)->nextTask) {
  296.                     (*prevTask)->nextTask = (*thisTask)->nextTask;
  297.                     break;
  298.                 }
  299.                 prevTask = (*prevTask)->nextTask;
  300.             }
  301.             if (thisTask == latestTask && prevTask) latestTask = prevTask;
  302.         }
  303.  
  304.         /* Release memory and decrement the task counter */
  305.         deleteObject((OBJECT*)thisTask);
  306.         taskCount--;
  307.         return(TRUE);    /* Task deleted successfully */
  308.     }
  309.     else return(FALSE);        /* Task is currently running and cannot be deleted */
  310. }
  311.  
  312.  
  313. /* 
  314.     This is a stronger version of 'deleteTask' which will 
  315.     delete a task even if the task was currently running.
  316.  
  317.     At least one task must however remain running, because otherwise
  318.     the system would crash.
  319. */
  320. int killTask(thisTask)
  321. TASK** thisTask;
  322. {
  323.     if (yieldingSuspend(thisTask))
  324.          return(deleteTask(thisTask));
  325.     else return(FALSE);     /* Cannot kill the only active task in the system */
  326. }
  327.  
  328.  
  329. /* 
  330.     These special stack operations are needed to allow the 
  331.     stacks of tasks to be initialized from other tasks.
  332. */
  333.  
  334. /* Push a value to the data stack of the given task */
  335.  
  336. void toTaskData(thisTask, data)
  337. TASK**    thisTask;
  338. int        data;
  339. {
  340.   TASK** tempUp = up;
  341.     yieldTo(thisTask);
  342.     pushData(data);
  343.     yieldTo(tempUp);
  344. }
  345.  
  346.  
  347. /* Push a value to the return stack of the given task */
  348. /* The task must not be active at the moment */
  349.  
  350. void toTaskReturn(thisTask, data)
  351. TASK**    thisTask;
  352. int        data;
  353. {
  354.     if (!isActivated(thisTask)) {
  355.         TASK** tempUp = up;
  356.         yieldTo(thisTask);
  357.         pushReturn((int*)data);
  358.         yieldTo(tempUp);
  359.     }
  360. }
  361.  
  362.  
  363. /* Push a value to the context stack of the given task */
  364. /* The task must not be active at the moment */
  365.  
  366. void toTaskCtxt(thisTask, data)
  367. TASK**    thisTask;
  368. int        data;
  369. {
  370.     if (!isActivated(thisTask)) {
  371.         TASK** tempUp = up;
  372.         yieldTo(thisTask);
  373.         pushContext((int*)data);
  374.         yieldTo(tempUp);
  375.     }
  376. }
  377.  
  378.  
  379. /* resizeDataStack(): resize the data stack of the given task */
  380. /* Since the location of stack's storage area may change, we must */
  381. /* preserve the stack pointer using an offset */
  382. /* The extra underflow area is added to the size */
  383. void resizeDataStack(thisTask, newSize)
  384. TASK** thisTask;
  385. int    newSize;
  386. {
  387.   OBJECT* stack = (*thisTask)->dataStack;
  388.   TASK**  tempUp = up;
  389.   int       ptrOffset;
  390.   
  391.     yieldTo(thisTask);
  392.     ptrOffset = (int*)dataSp - (int*)stack->mfa;
  393.     resizeClosure(stack, newSize + DATAOFFSET + UNDERFLOWRESERVE);
  394.     dataSp = (int*)stack->mfa + ptrOffset;
  395.     yieldTo(tempUp);
  396. }
  397.  
  398.  
  399. /* resizeReturnStack(): resize the return stack of the given task */
  400.  
  401. void resizeReturnStack(thisTask, newSize)
  402. TASK** thisTask;
  403. int    newSize;
  404. {
  405.   OBJECT* stack = (*thisTask)->returnStack;
  406.   TASK**  tempUp = up;
  407.   int      ptrOffset;
  408.     
  409.     yieldTo(thisTask);
  410.     ptrOffset = (int*)returnSp - (int*)stack->mfa;
  411.     resizeClosure(stack, newSize + DATAOFFSET + UNDERFLOWRESERVE);
  412.     returnSp = (int**)((int*)stack->mfa + ptrOffset);
  413.     yieldTo(tempUp);
  414. }
  415.  
  416.  
  417. /* resizeContextStack(): resize the context stack of the given task */
  418.  
  419. void resizeContextStack(thisTask, newSize)
  420. TASK** thisTask;
  421. int newSize;
  422. {
  423.   OBJECT* stack = (*thisTask)->contextStack;
  424.   TASK**  tempUp = up;
  425.   int      ptrOffset;
  426.     
  427.     yieldTo(thisTask);
  428.     ptrOffset = (int*)contextSp - (int*)stack->mfa;
  429.     resizeClosure(stack, newSize + DATAOFFSET + UNDERFLOWRESERVE);
  430.     contextSp = (int**)((int*)stack->mfa + ptrOffset);
  431.     yieldTo(tempUp);
  432. }
  433.  
  434.  
  435. /* Get the "current working directory" (CWD) of the desired task */
  436. /* CWD = the bottommost value in the context stack (see 'global.h') */
  437.  
  438. OBJECT* getTaskCWD(thisTask)
  439. TASK** thisTask;
  440. {
  441.   OBJECT* self;
  442.  
  443.     TASK**    tempUp = up;
  444.     up = thisTask;
  445.     self = (OBJECT*)CWD;
  446.     up = tempUp;
  447.     return(self);
  448. }
  449.  
  450.  
  451. /* Set the "current working directory" (CWD) of the desired task */
  452. /* CWD = the bottommost value in the context stack (see in 'global.h') */
  453.  
  454. void setTaskCWD(thisTask, self)
  455. TASK**    thisTask;
  456. OBJECT*    self;
  457. {
  458.      TASK** tempUp = up;
  459.     up = thisTask;
  460.     CWD = (int*)self;
  461.     up = tempUp;
  462. }
  463.  
  464.